package com.jackie.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Tuple;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * Created by jackie on 2017/2/27.
 */

public class ReadRankingList {

    /**
     * 模拟阅读文章排行榜，日/周/月度/年度阅读排行榜，点赞数排行榜
     */

    public static final int POSTACTICLE = 0;
    public static final int VOTE = 1;
    public static final int READ = 2;
    public static final int COMMENT = 3;
    public static final int DELETE = 4;
    public static final int OFFlINE = 5;

    /**
     * 发表文章
     *
     * @param userId
     */
    public static void postArticle(int userId) {
        Jedis jedis = RedisFactory.getJedis();
        String articleId = UUID.randomUUID().toString();
//        System.out.println(articleId);
        Map<String, String> article = new HashMap<>();
//        article.put("id",articleId.toString());
        article.put("title", "title" + articleId);
        article.put("content", "content" + articleId);
        article.put("author", "" + userId);
        article.put("postTime", "" + System.currentTimeMillis());
        jedis.hmset("user:" + userId + ":article:" + articleId, article);//用户文章详情
        jedis.sadd("user:" + userId + ":articles", articleId);//文章列表
//        System.out.println(jedis.hget("user:" + userId + ":article:" + articleId,"title"));
        RedisFactory.releaseJedis(jedis);
    }

    /**
     * 投票点赞/评论/阅读
     * @param userId
     * @param action votes/comments/views
     */
    public static void action(int userId,String action){
        Jedis jedis = RedisFactory.getJedis();
        //找到任一用户的任一文章
        String articleUserId = jedis.srandmember("users");
        if(articleUserId!=null){
            String articleId = jedis.srandmember("user:" + articleUserId + ":articles");
            if(articleId!=null){
                Transaction t = jedis.multi();
                t.sadd(action+":article:" + articleId, userId + "");
                t.zincrby(action, 1, "user:" + articleUserId + ":article:" + articleId);
                t.exec();
            }
        }
        RedisFactory.releaseJedis(jedis);
    }

    /**
     * 删除文章
     *
     * @param userId
     */
    public static void delete(int userId) {
        Jedis jedis = RedisFactory.getJedis();
        //删除自己的任一文章
        String articleId = jedis.srandmember("user:" + userId + ":articles");
        if(articleId!=null) {
            Transaction t = jedis.multi();
            t.del("votes:article:" + articleId);
            t.del("views:article:" + articleId);
            t.del("comments:article:" + articleId);
            t.zrem("votes", "user:" + userId + ":article:" + articleId);
            t.zrem("views", "user:" + userId + ":article:" + articleId);
            t.zrem("comments", "user:" + userId + ":article:" + articleId);
            t.srem("user:" + userId + ":articles", articleId);
            t.del("user:" + userId + ":article:" + articleId);
            t.exec();
        }
        RedisFactory.releaseJedis(jedis);
    }

    /**
     * 登陆
     *
     * @param userId
     */
    public static void login(int userId) {
        Jedis jedis = RedisFactory.getJedis();
        jedis.sadd("users", userId + "");
        RedisFactory.releaseJedis(jedis);
    }

    /**
     * 显示排行榜
     */
    public static void rankList() {
        Jedis jedis = RedisFactory.getJedis();
        System.out.println("====投票点赞排行榜=====");
        for (Tuple s : jedis.zrevrangeWithScores("votes", 0, 10)
                ) {
            System.out.println(jedis.hget(s.getElement(), "title") + ":" + s.getScore());
        }
        int votes = 0;
        for (Tuple s : jedis.zrangeWithScores("votes", 0, -1)
                ) {
            votes += s.getScore();
        }
        System.out.println("---投票点赞总数为"+votes+"----\n");
        /////////////////////////////////////////////////
        System.out.println("====评论排行榜=====");
        for (Tuple s : jedis.zrevrangeWithScores("comments", 0, 10)
                ) {
            System.out.println(jedis.hget(s.getElement(), "title") + ":" + s.getScore());
        }
        int comments = 0;
        for (Tuple s : jedis.zrangeWithScores("comments", 0, -1)
                ) {
            comments += s.getScore();
        }
        System.out.println("---评论总数为"+comments+"----\n");
        /////////////////////////////////////////////////////////
        System.out.println("====阅读排行榜=====");
        for (Tuple s : jedis.zrevrangeWithScores("views", 0, 10)
                ) {
            System.out.println(jedis.hget(s.getElement(), "title") + ":" + s.getScore());
        }
        int views = 0;
        for (Tuple s : jedis.zrangeWithScores("views", 0, -1)
                ) {
            views += s.getScore();
        }
        System.out.println("---阅读总数为"+views+"----\n");

        RedisFactory.releaseJedis(jedis);
    }

    public static int behavior() {
        int random = new Random().nextInt(100);
//        System.out.println("random:" + random);
        if (random < 5) return POSTACTICLE;//5%
        if (random < 20) return VOTE;//15%
        if (random < 70) return READ;//50%
        if (random < 85) return COMMENT;//15%
        if (random < 98) return OFFlINE;//13%
        return DELETE;//2%
    }

    public static void main(String[] args) throws TimeoutException {
        Jedis jedis = RedisFactory.getJedis();
        jedis.flushDB();
        RedisFactory.releaseJedis(jedis);
        System.out.println("begin...");
        //模拟500个用户同时在线,并模拟用户行为
        ExecutorService fixExecutor = Executors.newFixedThreadPool(50);
        Map<Integer, Future> userMap = new ConcurrentHashMap<>();
        //userMap=Collections.synchronizedMap(userMap);
        for (int i = 0; i < 50000; i++) {
//            System.out.println("用户" + i + "上线");
            login(i);
            Future future = fixExecutor.submit(new UserThread(i));
            userMap.put(i, future);
        }
        System.out.println("所有用户已上线...");
        while (!userMap.isEmpty()) {
            for (Map.Entry entry : userMap.entrySet()
                    ) {
                Future future = (Future) entry.getValue();
                try {
//                    if(future.get()==null) System.out.println("null");
                    if (future.isDone() && future.get(1, TimeUnit.MILLISECONDS).equals(OFFlINE)) {
                        Integer key = (Integer) entry.getKey();
                        userMap.remove(key);
//                        System.out.println("用户" + key + "已下线");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("所有用户已下线");
        rankList();
        fixExecutor.shutdown();
    }

    static class UserThread implements Callable {

        final int userId;

        UserThread(int userId) {
            this.userId = userId;
        }

        @Override
        public Object call() throws Exception {
            int b;
            while ((b = behavior()) != OFFlINE) {
                switch (b) {
                    case POSTACTICLE:
//                        System.out.println("用户" + userId + "正在发表文章");
                        postArticle(userId);
                        break;
                    case VOTE:
//                        System.out.println("用户" + userId + "正在投票点赞");
                        action(userId,"votes");
                        break;
                    case READ:
//                        System.out.println("用户" + userId + "正在阅读文章");
                        action(userId,"views");
                        break;
                    case COMMENT:
//                        System.out.println("用户" + userId + "正在评论");
                        action(userId,"comments");
                        break;
                    case DELETE:
//                        System.out.println("用户" + userId + "正在删除文章");
                        delete(userId);
                        break;
                    default:
                        break;
                }
            }
            return OFFlINE;
        }
    }
}
